Welcome

This is part 1 of the 2 part course from CDRC on using the UK Retail Centres dataset to create retail catchments. The video in this part introduces the Retail Centres data set, and the practical session shows you how to work with the Retail Centres dataset in R. If you are completely new to RStudio, please check out our Short Course on Using R as a GIS.

After completing this material, you will:

Setup

First we want to set up the libraries that we are going to be using for this practical - most of them should be familiar (e.g. dplyr, sf, tmap). However, the hereR library will likely be new - we are going to use this later, so install it for now and we will come back to it later.

library(sf)
library(dplyr)
library(tmap)
# install.packages("hereR")
library(hereR)

Make sure you have set your working directory to wherever you have stored the CDRC-Retail-Catchment-Training folder we have provided:

#setwd("CDRC-Retail-Catchment-Training")

1. Data (& Preprocessing)

The first dataset we are going to be using is a geopackage containing the updated Retail Centre boundaries for the UK, which is within your data packs for this practical. We have given you a subset for the Liverpool City Region (LCR), containing the top 20 Retail Centres within the LCR.

All datasets you need for this practical are contained within the 'Data' subfolder within the CDRC-Retail-Catchment-Training folder we provided.

## Read in the Retail Centre Boundaries 
rc <- st_read("Data/LCR_Retail_Centres_2018.gpkg")
## Reading layer `LiverpoolCR_Training2' from data source `C:\Users\sgpballa\Google Drive\Patrick Academic\POSTGRAD\CDRC Retail Project\Retail Training\CDRC-Retail-Catchment-Training\Data\LCR_Retail_Centres_2018.gpkg' using driver `GPKG'
## Simple feature collection with 20 features and 5 fields
## geometry type:  POLYGON
## dimension:      XY
## bbox:           xmin: 321011 ymin: 381441.1 xmax: 352342.2 ymax: 418208.7
## CRS:            27700

Let's take a look at the Retail Centre data:

## head() displays the first few rows of a dataframe/simple feature
head(rc)
## Simple feature collection with 6 features and 5 fields
## geometry type:  POLYGON
## dimension:      XY
## bbox:           xmin: 329579.5 ymin: 387801 xmax: 339819.7 ymax: 407399.8
## CRS:            27700
##         rcID n.units n.comp.units         rcName RetailPark
## 1 RC_EW_2713     189           44       Wallasey          N
## 2 RC_EW_2720      76           25         Formby          N
## 3 RC_EW_2723      75           28       Waterloo          N
## 4 RC_EW_2746     140           27  Allerton Road          N
## 5 RC_EW_2751     957          257 Liverpool City          N
## 6 RC_EW_2756     308           92     Birkenhead          N
##                             geom
## 1 POLYGON ((330413.8 391857.4...
## 2 POLYGON ((329908.9 407152.4...
## 3 POLYGON ((332200.5 398543.7...
## 4 POLYGON ((339115.6 388386, ...
## 5 POLYGON ((334054.8 390283.9...
## 6 POLYGON ((331231.6 388547.5...

So for each Retail Centre (polygon):

Let's map the Retail Centre polygons. Throughout this practical we will be using the tmap R package for all exploratory mapping - for more info on tmap visit: https://github.com/mtennekes/tmap.

In the next chunk of code i first setup the plotting mode i want to use throughout this practical (tmap_mode). The default option is "plot" which will produce any of your plots over a plain backgrond. By setting tmap_mode to "view" your layers are plotted automatically over an interactive leaflet basemap. Then I plot the Retail Centres by calling the rc object in the tm_shape() command, before specifying tm_fill() as the data is polygons not points or lines. Finally, i call tm_text() to label each of the polygons, specifying that i want the labels to be drawn from the rcName column - containing the names of the Retail Centres.

## Setup plotting
tmap_mode("view")

## Map Retail Centre Polygons for LCR
tm_shape(rc) +
  tm_fill(col = "orange") +
  tm_text("rcName", size = 0.75)

For the purpose of this analysis we want to extract the centroids of the Retail Centres to work with and construct catchments from. For those unfamiliar to centroids, they enable extraction of points from polygons, by taking the central point of the polygon. It is common practice when constructing catchments to do so with point data, so in this sense centroids are very helpful. The sf package has a neat st_centroid() function for extracting centroids from polygons.

## Extract centroids
rc_cent <- st_centroid(rc)

Let's map the Retail Centre Centroids:

## Map the centroids - note: tm_dots() is used as the object rc_cent contains point data (Retail Centre centroids)
tm_shape(rc_cent) +
  tm_dots(col = "orange") +
  tm_text("rcName", size = 0.75)

2. Creating a Retail Hierarchy

In this section we are going to build a three-tier hierarchy of Retail Centres. The hierarchy will be based on two things: the total number of comparison units in the centre (n.comp.units), and whether or not the Retail Centre is considered a major retail park.

In this next chunk of code I use the mutate function and case_when statements to generate a new column called 'hierarchy' where the Retail Centres are assigned as being 'primary', 'secondary' or 'tertiary' based on the number of comparison units in a centre (n.comp.units) and the value for Retail Park.

The conditions are set as follows:

Notice how I am using & and | in the case_when statements. For example, in the first assignment (tertiary), for a centre to be tertiary it has to have less than 50 comparison units and not be a Retail Park ("N"). In the second example, we assign secondary centres as either those with comparison unit count between 50 and 99 OR those that are Retail Parks ("Y"). Notice how i have used the | symbol to account for the OR condiiton in the assignment of secondary centres.

## Use mutate and case_when to create the new column - notice how ~ assigns the new values based on the condition
rc_cent_nopipes <-  mutate(rc_cent, 
                           hierarchy = dplyr::case_when(n.comp.units < 50 & RetailPark == "N" ~ "tertiary",
                                                       (n.comp.units >= 50 & n.comp.units < 100) | RetailPark == "Y" ~ "secondary",
                                                        n.comp.units >= 100 ~ "primary"))

I then want to extract only certain columns from the data, which i can achieve using the select() function from dplyr:

## Use select to extract the id, n.units, n.comp.units and hierarchy columns
rc_cent_nopipes <- select(rc_cent_nopipes, rcID, rcName, n.units, n.comp.units, RetailPark, hierarchy)

However, there is an alternative to writing two separate chunks of code to create the hierarchy and then extract the columns we want. You may have seen pipes before - %>% - as they are commonly used to integrate various dplyr functions together. They work by taking the output from one function (e.g. the creation of the hierarchy column with mutate) and feed it into the first argument of the next function (e.g. selection of columns with select). I'll show you how you can do it below:

## Use pipes to create the new hierarchy column and then select the other columns we are interested in
rc_cent <- rc_cent %>% 
  mutate(hierarchy = dplyr::case_when(n.comp.units < 50 & RetailPark == "N" ~ "tertiary",
                                                       (n.comp.units >= 50 & n.comp.units < 100) | RetailPark == "Y" ~ "secondary",
                                                        n.comp.units >= 100 ~ "primary")) %>%
  select(rcID, rcName, n.units, n.comp.units, RetailPark, hierarchy)

Notice how when piping you don't need to tell the mutate() and select() functions what object you want to perform that operation on, as it is piping directly from rc_cent. For clarity, let's compare the output with and without pipes:

## Without pipes
head(rc_cent_nopipes)
## Simple feature collection with 6 features and 6 fields
## geometry type:  POINT
## dimension:      XY
## bbox:           xmin: 329796 ymin: 388345.3 xmax: 339351.7 ymax: 407204.2
## CRS:            27700
##         rcID         rcName n.units n.comp.units RetailPark hierarchy
## 1 RC_EW_2713       Wallasey     189           44          N  tertiary
## 2 RC_EW_2720         Formby      76           25          N  tertiary
## 3 RC_EW_2723       Waterloo      75           28          N  tertiary
## 4 RC_EW_2746  Allerton Road     140           27          N  tertiary
## 5 RC_EW_2751 Liverpool City     957          257          N   primary
## 6 RC_EW_2756     Birkenhead     308           92          N secondary
##                        geom
## 1   POINT (330617 391938.6)
## 2   POINT (329796 407204.2)
## 3   POINT (332068 398533.3)
## 4 POINT (339351.7 388345.3)
## 5 POINT (334655.1 390278.6)
## 6 POINT (331913.4 388629.2)
## With pipes
head(rc_cent)
## Simple feature collection with 6 features and 6 fields
## geometry type:  POINT
## dimension:      XY
## bbox:           xmin: 329796 ymin: 388345.3 xmax: 339351.7 ymax: 407204.2
## CRS:            27700
##         rcID         rcName n.units n.comp.units RetailPark hierarchy
## 1 RC_EW_2713       Wallasey     189           44          N  tertiary
## 2 RC_EW_2720         Formby      76           25          N  tertiary
## 3 RC_EW_2723       Waterloo      75           28          N  tertiary
## 4 RC_EW_2746  Allerton Road     140           27          N  tertiary
## 5 RC_EW_2751 Liverpool City     957          257          N   primary
## 6 RC_EW_2756     Birkenhead     308           92          N secondary
##                        geom
## 1   POINT (330617 391938.6)
## 2   POINT (329796 407204.2)
## 3   POINT (332068 398533.3)
## 4 POINT (339351.7 388345.3)
## 5 POINT (334655.1 390278.6)
## 6 POINT (331913.4 388629.2)

The two outputs are identical. Whether you choose to use pipes is up to you - they can really speed up data transformation, but at the same time make it harder to untangle some broken code. For more information and tutorials on piping and how to use it with different dplyr functions, please visit: https://seananderson.ca/2014/09/13/dplyr-intro/

Ok, so from the rc_cent object you can see that each Retail Centre has now been assigned a hierarchical category, which we will use throughout the rest of the practical in determing catchments for the centres, that differ based on the hierarchical position of the centres.

3. Catchments (1) - Fixed-Ring Buffers

Fixed-Ring Buffers are the simplest catchments, and involve drawing a circular buffer around a store (or retail centre) based on a fixed value of distance, which accounts for the expected distances consumers are willing to travel. Fixed-Ring bufferscan be easily delineated in R using the st_buffer() function, where you give the function a simple features point object and distance (dependent on projection), and it returns a buffer for each individual point.

## Extract a 1000m buffer for each Retail Centre
buffer1km <- st_buffer(rc_cent, 1000)

Let's map these to see what they look like. Note: i am setting alpha to 0.3 so I can see overlapping buffers clearly.

## Map the buffers
tm_shape(buffer1km) + ## Plot the buffers
  tm_fill(col = "orange", alpha = 0.3) +
  tm_shape(rc_cent) + ## Overlay the centroids
  tm_dots(col = "orange")

However, it is more informative to have buffers that vary in size depending on the hierarchical position of the Retail Centres. In this next chunk of code I have created a small function to do this. Functions are really helpful tools that enable you to apply a series of methods in sequence and return the output. This is particularly helpful when you need to repeat steps over and over again, as it removes the need for typing out the same lines of code multiple times. For those new to functions, watch the short video below:

The function i have created delineates a 1000m catchment for Retail Centres classed as primary. It has three steps:

To use the function you need to run this next chunk of code to save the function to your environment, and then you can apply the function in a subsequent line of code to your dataset, to extract the catchments.

## Run this chunk to save the function to your environment
get_primary_buffer <- function(centroids) {
  
  ## Extracts centroids of 'primary' Retail Centres
  rc_primary <- filter(centroids, hierarchy == "primary")
  
  ## Constructs a 1000m buffer
  primary_buffer <- st_buffer(rc_primary, dist = 1000)
  
  ## Return the 1000m buffer
  return(primary_buffer)
}

So now that i have the get_primary_buffer function saved under 'functions' in my environment pane, I can run it on my dataset to extract primary catchments.

## Get primary buffers for LCR Retail Centres
buffer1000 <- get_primary_buffer(rc_cent)

Let's plot these:

## Plot the primary buffers and all the centroids, to check only primary centres have catchments
tm_shape(buffer1000) +
  tm_fill(col = "orange", alpha = 0.3) +
  tm_shape(rc_cent) +
  tm_dots(col = "orange") +
  tm_text("rcName", size = 0.75)

Notice how we have buffers for only the Liverpool City and Southport Retail Centres - as these are the only primary centres in this dataset.

However, if we wanted to build buffers that vary in size depending on the three different hierarchy categories, we would need to build a more complex function. The function below (get_buffer) does the same as the get_primary_buffer function, but builds catchments for primary, secondary and tertiary centres that differ in size. I have also included some additional arguments (primary_dist, secondary_dist, tertiary_dist) that enable you to modify the size of the buffer for each type of centre.

Note: An additional step is included in the get_buffer function - rbind() is used to row bind the primary, secondary and tertiary buffers together into one object that is returned.

So, run the next chunk to save the get_buffer function to your environment:

## Run this chunk to save the function to your environment
get_buffer <- function(centroids, primary_dist = 5000, secondary_dist = 3000, tertiary_dist = 1500) {
  
  ## Split up the Retail Centres based on hierarchy
  rc_primary <- filter(centroids, hierarchy == "primary")
  rc_secondary <- filter(centroids, hierarchy == "secondary")
  rc_tertiary <- filter(centroids, hierarchy == "tertiary")
  
  ## Run the buffer for the different Retail Centre hierarchies separately
  primary_buffer <- st_buffer(rc_primary, dist = primary_dist)
  secondary_buffer <- st_buffer(rc_secondary, dist = secondary_dist)
  tertiary_buffer <- st_buffer(rc_tertiary, dist = tertiary_dist)
  
  ## Join together
  buffer <- rbind(primary_buffer, secondary_buffer, tertiary_buffer)
  return(buffer) ## Return
}

Now run the function using the default buffer distances, these we feel are a good representation of the distances most people would be willing to travel to access the different types of centres:

## Run the function
hbuffer <- get_buffer(rc_cent, primary_dist = 5000, secondary_dist = 3000, tertiary_dist = 1500)

Excellent! Now map these to see what they look like:

tm_shape(hbuffer)+ ## Plot the varying fixed-ring buffers
  tm_fill(col = "hierarchy", palette = c("yellow", "orange", "red"), alpha = 0.5) + # Setting col to 'hierarchy' tells tmap to generate a different colour buffer for each value in the hierarchy column
  tm_shape(rc_cent) + ## Overlay the centroids
  tm_dots(col = "orange", alpha = 0.75) +
  tm_text("rcName", size = 0.75)

4. Catchments (2) - Drive-Time Catchments

In this section we are going to use the HERE API to delineate drive-time catchments for the Retail Centres. You need to make sure you have the 'hereR' package installed, and then need to get an API key to use the functions within the package.

To get an API key:

## Set API key
#set_key("insert-key-here")

NOTE: This API key will only be valid for a few hours at a time. If you receive this error in this next section: "Request 'id = 1' failed Status 401." you will need to go back to the HERE website and generate a new API key - if you already have two in use, just delete these on the site and you will be able to create additional API keys.

4a. Building Simple Drive-Time Catchments

Now you're good to go.

Drive-Time Catchments are similar to Fixed-Ring buffers in that they also apply a fixed measure (e.g. distance, time) from a store or retail centre. However, they are different in that they use digitised road networks, speed limits and transport modes to generate a (more accurate) polygon that represents the extent to which a vehicle can travel in all directions under a certain time limit or distance constraint.

The function we are going to be using for Drive-Time catchments is the isoline() function from the hereR package. You supply the function with a series of points, and can then set various parameters such as whether you want the catchment to be based on distance/time (range_type) and what transport mode you want the catchment to be based on (transport_mode).

Here is an example for the first Retail Centre in the dataset, where i set a 10-minute catchment based on car being the transport mode. The 'range' argument is where you select how you want to determine the maximum size of the catchment; in my case it's by time, and we set a value of 10 - i.e. 10 minutes Drive-Time. NOTE: each value you set for range must be multipled by 60 for the delineation to work correctly (e.g. range = (10 * 60)). We also set transport_mode to "car" for the purposes of the practical, but there are lots of options available in the isoline documentation

## Extract first Retail Centre
rc_a <- rc_cent[1, ]

## Extract the 10-minute driving catchment
iso_a <- isoline(rc_a, range = (10 * 60), range_type = "time", transport_mode = "car")

Let's map the isoline and its centroid to see what it looks like:

## Map the drive-time catchment for the first Retail Centre
tm_shape(iso_a) +
  tm_fill(col = "orange", alpha = 0.5) +
  tm_shape(rc_a) +
  tm_dots(col = "orange") +
  tm_text("rcName", size = 0.75)

To extract isolines for more than one point we need to add an additional argument to the isoline function (aggregate = FALSE), which prevents the API from combining each individual isoline into one multipolygon.

## Extract the 10-minute catchment for every Retail Centre in LCR
iso <- isoline(rc_cent, range = (10 * 60), range_type = "time", transport_mode = "car", aggregate = FALSE)

Map them:

## Map the 10-minute drive-time catchments for LCR Retail Centres
tm_shape(iso) +
  tm_fill(col = "orange", alpha = 0.3) +
  tm_shape(rc_cent) +
  tm_dots(col = "orange") +
  tm_text("rcName", size = 0.75)

4b. Drive-Time Catchments at Different Times/Days

An interesting element with building drive-time catchments is to what extent variations in date/time and traffic are taken into account when building the catchment. For example, a 10-minute drive-time catchment delineated for a Sunday morning is likely to be much larger than for a Friday evening during rush-hour traffic. To demonstrate this, we are going to implement two drive-time catchments for two different days/times of the week.

To set this up, we need to use the datetime function in the isoline() function, we will do it again with the first Retail Centre in the dataset. First for a Friday in rush hour:

## First set up the date & time you are interested in - e.g. Friday 5th Feb - 5pm 
friday5th <- as.POSIXct("2020-02-05 18:00:00 CET", tz = "Europe/Zurich")
## Build the catchment for Friday (rush hour)
friday_iso <- isoline(rc_a, range = (10 * 60), range_type = "time", transport_mode = "car",
                      datetime = friday5th)

Then for Sunday morning:

## First set up the date & time you are interested in - e.g. Sunday 7th - 6am
sunday7th <- as.POSIXct("2020-02-07 06:00:00 CET", tz = "Europe/Zurich")
## Build the catchment for Friday (rush hour)
sunday_iso <- isoline(rc_a, range = (10 * 60), range_type = "time", transport_mode = "car",
                      datetime = sunday7th)

Let's map them - we can build two maps individually saving them as p1 and p2.

## Make the Maps
p1 <- tm_shape(friday_iso) +
  tm_fill(col = "orange", alpha = 0.75) +
  tm_shape(rc_a) +
  tm_dots(col = "orange") +
  tm_text("rcName", size = 0.75)
p2 <- tm_shape(sunday_iso) +
  tm_fill(col = "orange", alpha = 0.75) +
  tm_shape(rc_a) +
  tm_dots(col = "orange") +
  tm_text("rcName", size = 0.75)

Now that we have the two maps saved as objects, we can plot them side-by-side, using the tmap_arrange() function. The function (from the tmap package) allows small maps to be arranged in a grid layout. Below i specify that i want them to be side-by-side (ncol = 2), but could stack them on top of eachother by swapping ncol to nrow = 2.

## Plot them side-by-side
tmap_arrange(p1, p2, ncol = 2)

So you can see a much larger catchment for 6am on a sunday morning in comparison to rush hour on a Friday evening. It is sometimes useful therefore to specify the time/day you want in the hereR API.

4c. Building Drive-Time Catchments that account for Retail Centre Hierarchy

The final approach we want to take is to use the hierarchies created in Section 2 to build drive-time catchments that vary depending on the position of the Retail Centre in the hierarchy. Similar to Section 3, I am going to use functions again to make drive-time catchments for the Retail Centres, which vary in size depending on the hierarchy of the Retail Centre.

As in Section 3 I will first show you how you can build a simplified function to extract drive-time catchments for the primary centres only.

The function below does four things:

  • Filters the centroids to get only 'primary' centres
  • Constructs a 15-minute drive-time catchment for the 'primary' centres
  • Merges on the Retail Centre data to the drive-time catchment
  • Returns 15-minute drive-time catchments for the primary centres

Note: i have included three additional arguments. range_type and transport_mode allow you to select the measure (e.g time, distance) and mode of transport (e.g. car, bike, bus) for the catchment, and dist controls the maximum size of the catchment based on the range_type you selected.

So run the next chunk of code to save the get_primary_drive_time() function to your environment:

## Function to get drive-time catchments for the primary Retail Centres
get_primary_drive_time <- function(centroids, dist = 15, range_type = "time", transport_mode = "car") {
  
  ## Filter the centroids to extract the primary centres
  rc_primary <- filter(centroids, hierarchy == "primary")
  
  ## Build the drive-time catchment
  primary_drive_time <- isoline(rc_primary, range = (dist * 60), 
                                range_type = range_type, transport_mode = transport_mode, aggregate = FALSE)
  
  ## Clean up the isoline - join on the Retail Centre information
  rc_primary <- rc_primary %>%
    as.data.frame() %>% 
    select(rcID, n.units, n.comp.units, hierarchy) %>%
    bind_cols(primary_drive_time) %>% ## Equivalent of cbind(), but for piping
    st_as_sf() ## Ensures final object is SF not dataframe
  return(rc_primary)
}

Now that we have the get_primary_drive_time function saved, we can run it on our data to build catchments for only the primary centres, note: I have set dist to 15 which means the function will return a 15-minute drive-time catchment.

## Get catchments for primary centres
primary_iso <- get_primary_drive_time(rc_cent, dist = 15, range_type = "time", transport_mode =  "car")

Now let's map them, and overlay the centroids to check only the primary centres have catchments.

## Map the primary drive-time catchments
tm_shape(primary_iso) +
  tm_fill(col = "orange", alpha = 0.5) +
  tm_shape(rc_cent) +
  tm_dots(col = "orange") +
  tm_text("rcName", size = 0.75)

So you can see that not all the centres have catchments, as our function is constructed to only delineate catchments for centres that are 'primary'. The next function fixes this, by repeating the steps in the previous function to extract secondary and tertiary catchments aswell as the primary ones, before joining these together to extract catchments for every centre in the dataset, using rbind() again.

Note: i have also included a primary_dist, secondary_dist and tertiary_dist argument so that the sizes of the catchments for each of the three types of centre can be modified. However, these default values we believe provide an accurate representation of the driving distances for each catchment.

## Run this chunk to save this function to your environment
get_drive_time <- function(centroids, primary_dist = 15, secondary_dist = 10, tertiary_dist = 5, 
                           range_type = "time", transport_mode = "car") {
  
  ## Split up the Retail Centres based on hierarchy
  rc_primary <- filter(centroids, hierarchy == "primary")
  rc_secondary <- filter(centroids, hierarchy == "secondary")
  rc_tertiary <- filter(centroids, hierarchy == "tertiary")
  
  ## Delineate the isolines for the different Retail Centre hierarchies separately
  primary_drive_time <- isoline(rc_primary, range = (primary_dist * 60),
                                range_type = range_type, transport_mode = transport_mode, aggregate = FALSE)
  secondary_drive_time <- isoline(rc_secondary, range = (secondary_dist * 60),
                                  range_type = range_type, transport_mode = transport_mode, aggregate = FALSE)
  tertiary_drive_time <- isoline(rc_tertiary, range = (tertiary_dist * 60),
                                 range_type = range_type, transport_mode = transport_mode, aggregate = FALSE)
  
  ## Join the Retail Centre info onto each set of catchments
  primary <- rc_primary %>%
    as.data.frame() %>%
    select(rcID, rcName, n.units, n.comp.units, hierarchy) %>%
    bind_cols(primary_drive_time)
  secondary <- rc_secondary %>%
    as.data.frame() %>%
    select(rcID, rcName, n.units, n.comp.units, hierarchy) %>%
    bind_cols(secondary_drive_time)
  tertiary <- rc_tertiary %>%
    as.data.frame() %>%
    select(rcID, rcName, n.units, n.comp.units, hierarchy) %>%
    bind_cols(tertiary_drive_time)
  
  ## Join catchments together
  isolines <- st_as_sf(rbind(primary, secondary, tertiary))
  return(isolines)
}

Now you can run the function. We want to extract a set of drive time catchments using different values for each level of the hierarchy - 15 minutes for primary centres, 10 minutes for secondary centres and 5 minutes for the tertiary centres.

## Extract the hierarchical drive time catchments for LCR Retail Centres
iso_hierarchy <- get_drive_time(rc_cent, primary_dist = 15, secondary_dist = 10, tertiary_dist = 5, 
                                range_type = "time", transport_mode = "car")

Great! Now you have drive-time catchments for the Retail Centres that account for the hierarchy. Let's map them to see what they look like:

tm_shape(iso_hierarchy) + ## Plot the hierarchical drive-time catchments 
  tm_fill(col = "hierarchy", palette = c("yellow", "orange", "red"), alpha = 0.5) + ## Setting col = 'hierarchy' tells tmap to plot a different colour for each value of hierarchy
  tm_shape(rc_cent) + ## Overlay the centroids
  tm_dots(col = "orange" ) +
  tm_text("rcName", size = 0.75)

4d. Optional Exercise - Using Pipes with tmap

A neat trick if you wanted to extract the catchment for one Retail Centre of interest (e.g. Liverpool Central), would be to using a filter prior to mapping. Remember that pipes automatically feed in the output from one step as the input to the next step, so you won't need to input anything into the tm_shape() argument if you've piped directly from iso_hierarchy.

## Filter to catchments of interest
iso_hierarchy %>%
  filter(rcName== "Liverpool City") %>%
  tm_shape() + ## Notice how tm_shape can be left empty as you have piped directly from the iso_hierarchy object
  tm_fill(col = "orange", alpha = 0.5) +
  tm_text("rcName", size = 0.75)

Writing Out Your Files

At this point we need to write out the Retail Centre centroids as they contain the 'Hierarchy' column and we will need this for Part 2 of this course.

## Write out to Data folder
# st_write(rc_cent, "Data/Part2_Retail_Centres.gpkg")

Summary

That's it! Ok so that's Part 1 of this practical completed, by now you should have a good understanding of:


This practical was written using R and RStudio by Patrick Ballantyne (P.J.Ballantyne@liverpool.ac.uk).

This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/deed.en.